<?php

/**
 * @copyright: ©2024 永州微聚商网络科技有限公司
 * @link: https://www.weijuyunke.com
 */
namespace app\common\service;

use think\facade\Log;
use app\admin\model\SystemConfig;
use WebSocket\Client;

class AIService
{

    private $url = 'https://spark-api-open.xf-yun.com';

    protected $api;

    protected $apiId;

    protected $apiKey;

    protected $apiSecret;

    /**
     * 架构函数
     *
     * @access public
     */
    public function __construct()
    {
        $set = sysconfig('account');
        
        $this->api = empty($set['account_api']) ? '' : $set['account_api'];
        $this->apiId = empty($set['account_apiid']) ? '' : $set['account_apiid'];
        $this->apiKey = empty($set['account_apikey']) ? '' : $set['account_apikey'];
        $this->apiSecret = empty($set['account_apisecret']) ? '' : $set['account_apisecret'];
    }

    function xfyun($words, $question)
    {
        if ($this->api == 'Spark Lite') {
            $addr = "wss://spark-api.xf-yun.com/v1.1/chat";
        } elseif ($this->api == 'Spark Pro') {
            $addr = "wss://spark-api.xf-yun.com/v3.1/chat";
        } elseif ($this->api == 'Spark Pro-128K') {
            $addr = "wss://spark-api.xf-yun.com/chat/pro-128k";
        } elseif ($this->api == 'Spark Max') {
            $addr = "wss://spark-api.xf-yun.com/v3.5/chat";
        } elseif ($this->api == 'Spark4.0 Ultra') {
            $addr = "wss://spark-api.xf-yun.com/v4.0/chat";
        }
        
        // 密钥信息，在开放平台-控制台中获取：https://console.xfyun.cn/services/cbm
        $Appid = $this->apiId;
        $Apikey = $this->apiKey;
        // $XCurTime =time();
        $ApiSecret = $this->apiSecret;
        // $XCheckSum ="";
        
        // $data = $this->getBody("你是谁？");
        $authUrl = $this->assembleAuthUrl("GET", $addr, $Apikey, $ApiSecret);
        // 创建ws连接对象
        $client = new Client($authUrl);
        
        // 连接到 WebSocket 服务器
        if ($client) {
            // 发送数据到 WebSocket 服务器
            $data = $this->getBody($Appid, $words, $question);
            $client->send($data);
            
            // 从 WebSocket 服务器接收数据
            $answer = "";
            $total_tokens = 0;
            $sid = '';
            while (true) {
                $response = $client->receive();
                $resp = json_decode($response, true);
                $code = $resp["header"]["code"];
                
                Log::write("AIService xfyun ->" . $response, 'info');
                
                if (0 == $code) {
                    $sid = $resp["header"]["sid"];
                    $status = $resp["header"]["status"];
                    if ($status != 2) {
                        $content = $resp['payload']['choices']['text'][0]['content'];
                        $answer .= $content;
                    } else {
                        $content = $resp['payload']['choices']['text'][0]['content'];
                        $answer .= $content;
                        $total_tokens = $resp['payload']['usage']['text']['total_tokens'];
                        Log::write("AIService xfyun 本次消耗token用量->" . $total_tokens, 'info');
                        break;
                    }
                } else {
                    return array(
                        'code' => 0,
                        'msg' => "服务返回报错" . $response
                    );
                    break;
                }
            }
            
            return array(
                'code' => 1,
                'data' => [
                    'sid' => $sid,
                    'tokens' => $total_tokens,
                    'answer' => $answer
                ],
                'msg' => 'success'
            );
        } else {
            return array(
                'code' => 0,
                'msg' => '无法连接到 WebSocket 服务器'
            );
        }
    }
    
    // 构造参数体
    function getBody($appid, $words, $question)
    {
        $header = array(
            "app_id" => $appid,
            "uid" => "12345"
        );
        
        if ($this->api == 'Spark Lite') {
            $domain = "general";
        } elseif ($this->api == 'Spark Pro') {
            $domain = "generalv3";
        } elseif ($this->api == 'Spark Pro-128K') {
            $domain = "pro-128k";
        } elseif ($this->api == 'Spark Max') {
            $domain = "generalv3.5";
        } elseif ($this->api == 'Spark4.0 Ultra') {
            $domain = "4.0Ultra";
        }
        
        $parameter = array(
            "chat" => array(
                "domain" => $domain,
                "temperature" => 0.5,
                "max_tokens" => $words
            )
        );
        
        $payload = array(
            "message" => array(
                "text" => array(
                    array(
                        "role" => "user",
                        "content" => $question
                    )
                )
            )
        );
        
        $json_string = json_encode(array(
            "header" => $header,
            "parameter" => $parameter,
            "payload" => $payload
        ));
        
        return $json_string;
    }
    // 鉴权方法
    function assembleAuthUrl($method, $addr, $apiKey, $apiSecret)
    {
        if ($apiKey == "" && $apiSecret == "") { // 不鉴权
            return $addr;
        }
        
        $ul = parse_url($addr); // 解析地址
        if ($ul === false) { // 地址不对，也不鉴权
            return $addr;
        }
        
        // // $date = date(DATE_RFC1123); // 获取当前时间并格式化为RFC1123格式的字符串
        $timestamp = time();
        $rfc1123_format = gmdate("D, d M Y H:i:s \G\M\T", $timestamp);
        // $rfc1123_format = "Mon, 31 Jul 2023 08:24:03 GMT";
        
        // 参与签名的字段 host, date, request-line
        $signString = array(
            "host: " . $ul["host"],
            "date: " . $rfc1123_format,
            $method . " " . $ul["path"] . " HTTP/1.1"
        );
        
        // 对签名字符串进行排序，确保顺序一致
        // ksort($signString);
        
        // 将签名字符串拼接成一个字符串
        $sgin = implode("\n", $signString);
        // 对签名字符串进行HMAC-SHA256加密，得到签名结果
        $sha = hash_hmac('sha256', $sgin, $apiSecret, true);
        $signature_sha_base64 = base64_encode($sha);
        
        // 将API密钥、算法、头部信息和签名结果拼接成一个授权URL
        $authUrl = "api_key=\"$apiKey\", algorithm=\"hmac-sha256\", headers=\"host date request-line\", signature=\"$signature_sha_base64\"";
        
        // 对授权URL进行Base64编码，并添加到原始地址后面作为查询参数
        $authAddr = $addr . '?' . http_build_query(array(
            'host' => $ul['host'],
            'date' => $rfc1123_format,
            'authorization' => base64_encode($authUrl)
        ));
        
        return $authAddr;
    }
}